package org.example.demoschedules.model;

import java.util.AbstractCollection;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

/**
 * 有序Map
 *
 * @author 单红宇
 * @since 2024-10-15 17:19:11
 */
@SuppressWarnings("all")
public class OrderedMap<K, V> {
    /**
     * map
     */
    private final Map<K, V> map;
    /**
     * orderList
     */
    private final LinkedList<K> orderList;

    /**
     * OrderedMap
     */
    public OrderedMap() {
        this.map = new HashMap<>();
        this.orderList = new LinkedList<>();
    }

    /**
     * put
     *
     * @param key   key
     * @param value value
     * @return V
     */
    public V put(K key, V value) {
        V oldValue = map.put(key, value);
        if (oldValue == null) {
            orderList.addLast(key);
        }
        return oldValue;
    }

    /**
     * get
     *
     * @param key key
     * @return V
     */
    public V get(Object key) {
        return map.get(key);
    }

    /**
     * remove
     *
     * @param key key
     * @return V
     */
    public V remove(Object key) {
        V value = map.remove(key);
        if (value != null) {
            orderList.remove(key);
        }
        return value;
    }

    /**
     * containsKey
     *
     * @param key key
     * @return data
     */
    public boolean containsKey(Object key) {
        return map.containsKey(key);
    }

    /**
     * size
     *
     * @return data
     */
    public int size() {
        return map.size();
    }

    /**
     * isEmpty
     *
     * @return data
     */
    public boolean isEmpty() {
        return map.isEmpty();
    }

    /**
     * firstEntry
     *
     * @return Entry
     */
    public Map.Entry<K, V> firstEntry() {
        return this.getFirstEntry();
    }

    /**
     * getFirstEntry
     *
     * @return Entry
     */
    public Map.Entry<K, V> getFirstEntry() {
        if (orderList.isEmpty()) {
            return null;
        }
        K firstKey = orderList.getFirst();
        return new AbstractMap.SimpleEntry<>(firstKey, map.get(firstKey));
    }

    /**
     * lastEntry
     *
     * @return Entry
     */
    public Map.Entry<K, V> lastEntry() {
        return this.getLastEntry();
    }

    /**
     * getLastEntry
     *
     * @return Entry
     */
    public Map.Entry<K, V> getLastEntry() {
        if (orderList.isEmpty()) {
            return null;
        }
        K lastKey = orderList.getLast();
        return new AbstractMap.SimpleEntry<>(lastKey, map.get(lastKey));
    }

    /**
     * getEntryAt
     *
     * @param index index
     * @return Entry
     */
    public Map.Entry<K, V> getEntryAt(int index) {
        if (index < 0 || index >= orderList.size()) {
            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + orderList.size());
        }
        K key = orderList.get(index);
        return new AbstractMap.SimpleEntry<>(key, map.get(key));
    }

    /**
     * keySet
     *
     * @return Set
     */
    public Set<K> keySet() {
        return new LinkedKeySet();
    }

    /**
     * entrySet
     *
     * @return Set
     */
    public Set<Map.Entry<K, V>> entrySet() {
        return new LinkedEntrySet();
    }

    /**
     * putAll
     *
     * @param m m
     */
    public void putAll(OrderedMap<? extends K, ? extends V> m) {
        for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }

    /**
     * nextKey
     *
     * @param key key
     * @return K
     */
    public K nextKey(K key) {
        int index = orderList.indexOf(key);
        if (index < 0 || index + 1 >= orderList.size()) {
            return null;
        }
        return orderList.get(index + 1);
    }

    /**
     * previousKey
     *
     * @param key key
     * @return K
     */
    public K previousKey(K key) {
        int index = orderList.indexOf(key);
        if (index <= 0) {
            return null;
        }
        return orderList.get(index - 1);
    }

    /**
     * lastKey
     *
     * @return K
     */
    public K lastKey() {
        return this.lastEntry().getKey();
    }

    /**
     * firstKey
     *
     * @return K
     */
    public K firstKey() {
        return this.firstEntry().getKey();
    }

    /**
     * values
     *
     * @return Collection
     */
    public Collection<V> values() {
        return new LinkedValueCollection();
    }

    @Override
    public String toString() {
        Iterator<Map.Entry<K,V>> i = entrySet().iterator();
        if (! i.hasNext())
            return "{}";

        StringBuilder sb = new StringBuilder();
        sb.append('{');
        for (;;) {
            Map.Entry<K,V> e = i.next();
            K key = e.getKey();
            V value = e.getValue();
            sb.append(key   == this ? "(this Map)" : key);
            sb.append('=');
            sb.append(value == this ? "(this Map)" : value);
            if (! i.hasNext())
                return sb.append('}').toString();
            sb.append(',').append(' ');
        }
    }

    /**
     * LinkedKeySet
     *
     * @author 单红宇
     * @since 2024-10-15 17:19:25
     */
    private class LinkedKeySet extends AbstractSet<K> {

        @Override
        public Iterator<K> iterator() {
            return orderList.iterator();
        }

        @Override
        public int size() {
            return orderList.size();
        }

        @Override
        public boolean contains(Object o) {
            return map.containsKey(o);
        }

        @Override
        public boolean remove(Object o) {
            if (map.remove(o) != null) {
                orderList.remove(o);
                return true;
            }
            return false;
        }
    }

    /**
     * LinkedEntrySet
     *
     * @author 单红宇
     * @since 2024-10-15 17:19:25
     */
    private class LinkedEntrySet extends AbstractSet<Map.Entry<K, V>> {
        @Override
        public Iterator<Map.Entry<K, V>> iterator() {
            return new Iterator<Map.Entry<K, V>>() {
                private final Iterator<K> keyIterator = orderList.iterator();

                @Override
                public boolean hasNext() {
                    return keyIterator.hasNext();
                }

                @Override
                public Map.Entry<K, V> next() {
                    K key = keyIterator.next();
                    return new AbstractMap.SimpleEntry<>(key, map.get(key));
                }

                @Override
                public void remove() {
                    K key = keyIterator.next();
                    map.remove(key);
                    keyIterator.remove();
                }
            };
        }

        @Override
        public int size() {
            return map.size();
        }
    }

    /**
     * LinkedValueCollection
     *
     * @author 单红宇
     * @since 2024-10-16 11:33:34
     */
    private class LinkedValueCollection extends AbstractCollection<V> {
        @Override
        public Iterator<V> iterator() {
            return new Iterator<V>() {
                private final Iterator<K> keyIterator = orderList.iterator();

                @Override
                public boolean hasNext() {
                    return keyIterator.hasNext();
                }

                @Override
                public V next() {
                    K key = keyIterator.next();
                    return map.get(key);
                }

                @Override
                public void remove() {
                    K key = keyIterator.next();
                    map.remove(key);
                    keyIterator.remove();
                }
            };
        }

        @Override
        public int size() {
            return map.size();
        }
    }
}
